/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.system;

import java.awt.Image;
import java.net.URL;
import tal.drivechain.chain.*;
import java.util.StringTokenizer;
import javax.swing.ImageIcon;

/**
 * Defines global constants, stores shared objects between modules and
 * maintains a list of every running Chain in the system.
 * 
 * @author Tal Eisenberg
 */
public class Globals {
    private static Chain rootChain = Chain.createRootChain();
    private static String commandLine;
    private static String scriptsPath = null;

    public static void setCommandLine(String command) {
        commandLine = command;
    }

    public static String getCommandLine() {
        return commandLine;
    }

    public static Chain findChain(String name, Chain ref) {
        ChainNode n = findNode(name, ref);
        if (!(n instanceof Chain))
            throw new IllegalArgumentException("Node " + n.getPath() + " is not a chain.");

        return (Chain)n;
    }
    
    public enum OS { Windows, Mac, Unknown}
    private static OS os = null;
    private static Image chainIcon = null;

    public static void setRootChain(Chain root) {
        rootChain = root;
    }
    
    /** Current version of the system + name */
    public final static Version VERSION = new Version("drivechain", Version.Release.PRE_ALPHA, new int[] {0,2,5});

    public static Chain getRootChain() {
        return rootChain;
    }

    public static Image getChainIcon() {
        if (chainIcon==null) {
            URL imgURL = new Globals().getClass().getResource("/resources/chain-icon.png");
            if (imgURL != null)
                chainIcon = new ImageIcon(imgURL, "icon").getImage();
            else throw new IllegalArgumentException("Can't find chain-icon.png");
        }
        return chainIcon;
    }
    
    /**
     * Returns a Chain object that represents a running chain according to its path.
     * It can also parse relative paths by using 'this' and 'parent'.
     * 
     * examples:
     * 
     * root.driveide.shell          (an absolute path to chain shell, assuming the root chain is called root)
     * this.parent.siblingchain     (a relative path to a sibling chain of the reference chain - this)
     * shell.response               (will look for this path under the reference chain)
     *
     * @param path The chain path to lookup.
     * @param thisReference This chain will be used as the reference chain. If null, using the "this" keyword will throw an exception.
     * @return a Chain object or null if no chain was found.
     */
    public static ChainNode findNode(String path, Chain thisReference) {
        //in the format of main.chain.chain....chain
        StringTokenizer t = new StringTokenizer(path, ".");
        if (t.countTokens()==0)
            throw new IllegalArgumentException("Missing chain name.");
        String root = t.nextToken();

        ChainNode current = null;
        if (root.equals("this")) {
            if (thisReference!=null)
                current = thisReference;
            else throw new IllegalArgumentException("Invalid use of 'this'. A reference chain is missing.");
        } else {
            if (!root.equals(Globals.getRootChain().getName())) {
                // try to see if a child of the reference chain matches
                if (thisReference!=null) {
                    for (ChainNode child : thisReference.getChildNodes().values()) {
                        if (child.getName().equals(root))
                            current = child;
                    }
                }
                if (current == null)
                    throw new IllegalArgumentException("Unknown node path: " + root + (thisReference!=null?" (from " + thisReference.getPath() + ")":""));
            } else current = Globals.getRootChain();
        }
        while (t.hasMoreTokens()) {
            boolean foundChild = false;
            String lookup = t.nextToken();
            if (lookup.equals("parent")) {
                Chain parent = current.getParentChain();
                if (parent==null)
                    throw new IllegalArgumentException("Node " + current.getName() + " doesn't have a parent chain (" + path + ").");
                current = current.getParentChain();
            }
            else { // try to find a child chain whose name matches the token.
                if (!(current instanceof Chain)) throw new IllegalArgumentException(" Node " + current.getName() + " is not a chain node and doesn't have child nodes.");
                for (ChainNode child : ((Chain)current).getChildNodes().values()) {
                    if (child.getName().equals(lookup)) {
                        current = child;
                        foundChild = true;
                        break;
                    }
                }
                if (!foundChild)
                    throw new IllegalArgumentException("Node '" + lookup + "' is not a child of chain " + current.getName());
            }
        }
        return current;
        
    }

    public static ChainNode findNode(String path) {
        return findNode(path, null);
    }
    /**
     * Adds a chain to the chains table.
     * 
     * @param chain a Chain object that represents a running chain.
     */

    public static OS getOS() {
        if (os==null) os = initOS();
        return os;
    }

    private static OS initOS() {
        if (System.getProperty("mrj.version")!=null)
            return OS.Mac;
        else if (System.getProperty("os.name").indexOf("windows")!=-1 ||
                System.getProperty("os.name").indexOf("nt")!=-1) {
            return OS.Windows;
        } else return OS.Unknown;
    }

    public static String getScriptsPath() { return scriptsPath; }
    public static void setScriptsPath(String scriptsPath) { Globals.scriptsPath = scriptsPath; }

    public static Version getVersion() { return VERSION; }
}
